package cn.zyl.demo.common.tools;

import cn.zyl.demo.common.enums.DateFormatEnum;
import cn.zyl.demo.common.enums.DateIndexEnum;
import cn.zyl.demo.common.exception.DateToolException;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;

/**
 * DateTool类说明:
 *
 * @author wsz
 * @version v1.0
 * @date 2020-07-22
 */
public class DateTool {
    private DateTool(){throw new IllegalAccessError("DateTool");}
    /** wsz-异常信息：日期对象为空*/
    private final static String DATE_IS_NULL_MSG = "Date is null!";
    /** wsz-异常信息：日期对象为空*/
    private final static String LOCAL_DATE_TIME_IS_NULL_MSG = "LocalDateTime is null!";
    /** wsz-异常信息：日期字符串对象为空*/
    private final static String DATE_STR_IS_NULL_MSG = "Date String is empty!";
    /** wsz-最小的日期*/
    private final static String MIN_DATE_STR ="1970-01-01 00:00:00";
    private final static long N_24 = 24L;
    private final static long N_60 = 60L;
    private final static long N_1000 = 1000L;
    private enum LimitEnum{
        /** wsz-时间常量：0*/
        ZERO(0),
        /** wsz-时间常量：年 最小值*/
        MIN_YEAR(1970),
        /** wsz-时间常量：月 最大值*/
        MAX_MONTH(13),
        /** wsz-时间常量：日期 最大值*/
        MAX_DAY(31),
        /** wsz-时间常量：小时 最大值*/
        MAX_HOUR(23),
        /** wsz-时间常量：分、秒 最大值*/
        MAX_MINUTE_SECOND(69),
        ;
        private final Integer num;
        LimitEnum(Integer num){ this.num = num; }
        public Integer getNum() { return num; }
    }
    /**
     * 获取当前日期
     * @param pattern 格式化参数
     * @return 日期字符串
     */
    public static String getNow(DateFormatEnum pattern) {
        return format(new Date(), pattern);
    }
    /**
     * 日期计算，加毫秒
     * @param date 日期
     * @param interval 毫秒
     * @return 计算的日期
     */
    public static Date add(Date date,long interval) {
        return new Date(date.getTime() + interval);
    }
    /**
     * Date转换为格式化时间
     * @date 2020年6月16日
     * @param date date
     * @param pattern 格式
     * @return String
     */
    public static String format(Date date, DateFormatEnum pattern){
        DateToolException.isTrueThrow(null == date,DATE_IS_NULL_MSG);
        return formatLocalDateTime(ofInstantDefault(date), pattern);
    }
    /**
     * localDateTime转换为格式化时间
     * @date 2020年6月16日
     * @param localDateTime localDateTime
     * @param pattern 格式
     * @return String
     */
    public static String formatLocalDateTime(LocalDateTime localDateTime, DateFormatEnum pattern){
        DateToolException.isTrueThrow(null == localDateTime,LOCAL_DATE_TIME_IS_NULL_MSG);
        DateTimeFormatter formatter = createFormatter(pattern);
        return localDateTime.format(formatter);
    }
    /**
     * 格式化字符串转为Date
     * @date 2020年6月16日
     * @param time 格式化时间
     * @param pattern 格式
     * @return Date
     */
    public static Date parseDate(String time, DateFormatEnum pattern){
        DateToolException.isTrueThrow(StringTool.isEmpty(time),DATE_STR_IS_NULL_MSG);
        return Date.from(parseLocalDateTime(time, pattern).atZone(ZoneId.systemDefault()).toInstant());
    }
    /**
     * 格式化字符串转为LocalDateTime
     * @date 2020年6月16日
     * @param time 格式化时间
     * @param pattern 格式
     * @return LocalDateTime
     */
    public static LocalDateTime parseLocalDateTime(String time,  DateFormatEnum pattern){
        DateToolException.isTrueThrow(StringTool.isEmpty(time),DATE_STR_IS_NULL_MSG);
        DateTimeFormatter formatter = createFormatter(pattern);
        return LocalDateTime.parse(time, formatter);
    }

    /**设置日期的时分秒，返回一个新的对象
     * @date 2020年6月16日
     * @param date 日期
     * @param hour 时0-23
     * @param minute 分0-59
     * @param second 秒0-59
     * @return 年月日时分秒，发生改变的日期
     */
    public static Date setHourOfMinuteOfSecond(Date date,Integer hour,Integer minute,Integer second){
        DateToolException.isTrueThrow(null == date,DATE_IS_NULL_MSG);
        return set(date,null,null,null,hour,minute,second);
    }
    /**设置日期的年月日，返回一个新的对象
     * @date 2020年6月16日
     * @param date 日期
     * @param year 年1997-
     * @param month 月1-12
     * @param day 日1-31
     * @return 年月日时分秒，发生改变的日期
     */
    public static Date setYearOfMonthOfDay(Date date,Integer year,Integer month,Integer day){
        DateToolException.isTrueThrow(null == date,DATE_IS_NULL_MSG);
        return set(date,year,month,day,null,null,null);
    }

    /**
     * 获取日期的年、月、日、时、分、秒等
     * @date 2020年6月16日
     * @param date 时间
     * @param type 年、月、日、时、分、秒等下标
     * @return Integer 年、月、日、时、分、秒
     */
    @SuppressWarnings("MagicConstant")
    public static Integer get(Date date, DateIndexEnum type){
        DateToolException.isTrueThrow(null == date,DATE_IS_NULL_MSG);
        Calendar c = toCalendar(date);
        return c.get(type.getIndex());
    }

    /**
     * 1970-当前时间，随机时间生成
     */
    public static String randomDateStr(DateFormatEnum pattern){
        return format(randomDate(),pattern);
    }
    /**
     * 1970-当前时间，随机时间生成
     */
    public static Date randomDate(){
        Date startDate = new Date();
        Date endDate = parseDate(MIN_DATE_STR,DateFormatEnum.y_M_d_H_m_s_1);
        return randomDate(startDate,endDate);
    }
    /**
     * 开始时间-截至时间，随机时间生成
     */
    public static String randomDateBetweenStr(String start,String end,DateFormatEnum pattern){
        return format(randomDateBetween(start, end,pattern),pattern);
    }
    /**
     * 开始时间-截至时间，随机时间生成
     */
    public static Date randomDateBetween(String start,String end,DateFormatEnum pattern){
        DateToolException.isTrueThrow(StringTool.isEmpty(start),DATE_STR_IS_NULL_MSG);
        DateToolException.isTrueThrow(StringTool.isEmpty(end),DATE_STR_IS_NULL_MSG);
        Date startDate = parseDate(start,pattern);
        Date endDate = parseDate(end,pattern);
        return randomDate(startDate,endDate);
    }
    //*********************************私有函数******************
    /**
     * Date 转 LocalDateTime，系统默认时区
     * @author wsz
     * @date 2020年6月16日
     * @param date 时间
     * @return LocalDateTime
     */
    private static LocalDateTime ofInstantDefault(Date date){
        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
    }
    /**
     * 获取日期格式化对象
     * @date 2020年6月16日
     * @param pattern 日期格式字符串
     * @return DateTimeFormatter
     */
    private static DateTimeFormatter createFormatter(DateFormatEnum pattern){
        return DateTimeFormatter.ofPattern(pattern.getPattern());
    }
    /**设置日期的年月日时分秒，返回一个新的对象
     * @date 2020年6月16日
     * @param date 日期，如果为null,就是当前时间
     * @param year 年1997-
     * @param month 月1-12
     * @param day 日1-31
     * @param hour 时0-23
     * @param minute 分0-59
     * @param second 秒0-59
     * @return 年月日时分秒，发生改变的日期
     */
    private static Date set(Date date,Integer year,Integer month,Integer day,Integer hour,Integer minute,Integer second) {
        Calendar c = toCalendar(date);
        if(null!=year && year >= LimitEnum.MIN_YEAR.getNum()){ c.set(Calendar.YEAR, year); }

        //默认月设置 0-11，-1变成 1-12
        if(null!=month && month >= LimitEnum.ZERO.getNum() && month <= LimitEnum.MAX_MONTH.getNum()){ c.set(Calendar.MONTH, month-1); }
        if(null!=day && day > LimitEnum.ZERO.getNum() && day <= LimitEnum.MAX_DAY.getNum()){ c.set(Calendar.DAY_OF_MONTH, day); }
        if(null!=hour && hour >= LimitEnum.ZERO.getNum() && hour <= LimitEnum.MAX_HOUR.getNum()){ c.set(Calendar.HOUR_OF_DAY, hour); }
        if(null!=minute && minute >= LimitEnum.ZERO.getNum() && minute <= LimitEnum.MAX_MINUTE_SECOND.getNum()){ c.set(Calendar.MINUTE, minute); }
        if(null!=second && second >= LimitEnum.ZERO.getNum() && second <= LimitEnum.MAX_MINUTE_SECOND.getNum()){ c.set(Calendar.SECOND, second); }
        return c.getTime();
    }

    /**
     * date 转 Calendar
     * @date 2020年6月16日
     * @param date 时间
     * @return Calendar，严格模式
     */
    private static Calendar toCalendar(Date date){
        if( null == date) { date = new Date();}
        Calendar c = Calendar.getInstance();
        //严格模式
        c.setLenient(false);
        c.setTime(date);
        return c;
    }
    /**
     * 开始时间-截至时间段，随机时间生成
     */
    private static Date randomDate(Date start,Date end){
        long s = start.getTime();
        long e = end.getTime();
        long r = (long) ( (Math.min(s, e)) + ( Math.random() * (s > e ? s-e : e-s) ) );
        return new Date(r);
    }

    /**
     * 获取当前年份
     * @return 当前年份
     */
    @SuppressWarnings("MagicConstant")
    public static Integer getCurrentYear() {
        return getCalendar(new Date()).get(DateIndexEnum.YEAR.getIndex());
    }
    /**
     * Date 转 jdk8 日期 Calendar
     * @param date 日期
     * @return jdk8 日期 Calendar
     */
    public static Calendar getCalendar(Date date) {
        Calendar calendar=Calendar.getInstance();
        calendar.setTime(date);
        return calendar;
    }

    /**
     * 计算年龄
     * @param birthDay 生日
     * @param pattern 日期格式
     * @return 年龄，正数
     */
    public static int getAge(String birthDay,DateFormatEnum pattern){
        LocalDateTime localDateTime = parseLocalDateTime(birthDay,pattern);
        LocalDateTime now = LocalDateTime.now();
        //出生日期晚于当前时间，无法计算
        if(localDateTime.isAfter(now)){ return -1; }
        //当前年月日
        int yearNow = now.getYear(),monthNow = now.getMonth().getValue(),dayOfMonthNow = now.getDayOfMonth();
        //生日的年月日
        int year = localDateTime.getYear(),month = localDateTime.getMonth().getValue(),dayOfMonth = localDateTime.getDayOfMonth();
        int age = yearNow - year;
        //当前月份在生日之前/当前日期在生日之前，年龄减一
        if ( monthNow < month) {
            age-=1;
        }else if (monthNow == month && dayOfMonthNow < dayOfMonth){
            age-=1;
        }
        return age;
    }
    /**
     * 毫秒数格式化 hh:mm:ss.SSS
     * <pre>
     *     时间换算：
     *     1小时(h)=60分钟
     *     1分钟(m)=60秒
     *     1秒(s)=1000毫秒
     *     1毫秒(ms)=1000微秒
     *     1微秒(μs)=1000纳秒
     *     1纳秒(ns)=1000皮秒(ps)
     * </pre>
     * @param millis 毫秒数
     * @return 格式化字符串
     */
    public static String longFormatByMillis(long millis){
        if(millis <= 0){return "00:00:00.000";}
        long s = millis/N_1000;
        long m = s/N_60;
        long h = m/N_60;
        return h + ":" + m % N_60 + ":" + s % N_60 +
                "." + millis % N_1000;
    }
    /**
     * 毫秒数格式化 hh:mm:ss.SSS,sss
     * <pre>
     *     时间换算：
     *     1小时(h)=60分钟
     *     1分钟(m)=60秒
     *     1秒(s)=1000毫秒
     *     1毫秒(ms)=1000微秒
     *     1微秒(μs)=1000纳秒
     *     1纳秒(ns)=1000皮秒(ps)
     * </pre>
     * @param nanoTime 纳秒数
     * @return 格式化字符串
     */
    public static String longFormatByNanoTime(long nanoTime){
        if(nanoTime <= 0){return "00:00:00.000,000`000";}
        StringBuilder res = new StringBuilder();
        long us = nanoTime/N_1000;
        long ms = us/N_1000;
        long s = ms/N_1000;
        long m = s/N_60;
        long h = m/N_60;
        res.append(h).append(":").append(m%N_60).append(":").append(s%N_60);
        res.append(".").append(ms%N_1000);
        res.append(",").append(us%N_1000);
        res.append("`").append(nanoTime%N_1000);
        return res.toString();
    }
    public static void main(String[] args) {
        System.out.println(getNow(DateFormatEnum.y_M_d));
        System.out.println(getNow(DateFormatEnum.y_M_d_H_m_s_1));
        Date now = new Date();
        //2020-06-16 12:04:52:829
        DateFormatEnum formatEnum = DateFormatEnum.y_M_d_H_m_s_S_1;
        System.out.println(format(now,formatEnum));
        //2020-06-16 00:00:00
        System.out.println(format(now,DateFormatEnum.y_M_d_0_0_0));
        //2020-06-16 23:59:59
        System.out.println(format(now,DateFormatEnum.y_M_d_23_59_59));
        //2020-06-01
        System.out.println(format(now,DateFormatEnum.y_M_01));
        //1997-01-01 00:00:00
        System.out.println(format(set(now, 1997, 1, 1, 0, 0, 0),formatEnum));
        //2020-12-31 23:59:59
        System.out.println(format(set(now, 2020, 12, 31, 23, 59, 59),formatEnum));
        //2020-06-10 11:45:00
        System.out.println(format(set(now, 2020, 6, 10, 11, 45, 00), formatEnum));
        //年：2020
        System.out.println("年："+get(now,DateIndexEnum.YEAR));
        //月：5
        System.out.println("月："+get(now,DateIndexEnum.MONTH));
        //日：16
        System.out.println("日："+get(now,DateIndexEnum.DAY_OF_MONTH));
        //时：11
        System.out.println("时："+get(now,DateIndexEnum.HOUR_OF_DAY));
        //分：2020
        System.out.println("分："+get(now,DateIndexEnum.MINUTE));
        //秒：16
        System.out.println("秒："+get(now,DateIndexEnum.SECOND));
        //毫秒：764
        System.out.println("毫秒："+get(now,DateIndexEnum.MILLISECOND));

        System.out.println("12324-format："+longFormatByMillis(12324L));
        System.out.println("12324123123-format："+longFormatByNanoTime(12324123123L));
    }
}
